{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6a925c67-ca70-4352-a481-ab08a57122f9",
   "metadata": {},
   "outputs": [],
   "source": [
    "%load_ext autoreload\n",
    "%autoreload 2\n",
    "%run notebook_setup.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c351e684-c495-465a-bfde-7e03888aff15",
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "import os\n",
    "import pprint\n",
    "from plotting import plt, plot_fills_long, create_forager_balance_figures\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "import json\n",
    "from config_utils import load_config, dump_config, format_config\n",
    "from backtest import (\n",
    "    prepare_hlcvs_mss,\n",
    "    run_backtest,\n",
    "    process_forager_fills,\n",
    "    build_backtest_payload,\n",
    "    execute_backtest,\n",
    "    subset_backtest_payload,\n",
    ")\n",
    "from collections import defaultdict\n",
    "\n",
    "pd.set_option(\"display.precision\", 4)           # affects Series repr\n",
    "pd.options.display.float_format = \"{:.4f}\".format  # consistent 4‑decimals everywhere\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "60155aa0-a1d3-4763-b314-5e015643b527",
   "metadata": {},
   "outputs": [],
   "source": [
    "config = load_config(\"configs/template.json\")\n",
    "# config[\"backtest\"][\"combine_ohlcvs\"] = True\n",
    "# config[\"backtest\"][\"start_date\"] = \"2023-03-01\"\n",
    "# config['backtest']['end_date'] = \"2025-03-20\"\n",
    "# config[\"backtest\"][\"exchanges\"] = [\"binance\", \"bybit\"]\n",
    "exchange = \"combined\" if config[\"backtest\"][\"combine_ohlcvs\"] else config[\"backtest\"][\"exchanges\"][0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4b7b1beb-b661-4871-9750-687d8eeafd3f",
   "metadata": {},
   "outputs": [],
   "source": [
    "coins, hlcvs, mss, results_path, cache_dir, btc_usd_prices, timestamps = await prepare_hlcvs_mss(config, exchange)\n",
    "config[\"backtest\"][\"coins\"] = {exchange: coins}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "245ff1a1",
   "metadata": {},
   "outputs": [],
   "source": [
    "payload = build_backtest_payload(\n",
    "    hlcvs=hlcvs,\n",
    "    mss=mss,\n",
    "    config=config,\n",
    "    exchange=exchange,\n",
    "    btc_usd_prices=btc_usd_prices,\n",
    "    timestamps=timestamps,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2f473002-2ba5-41e3-9f06-ff56aaea2318",
   "metadata": {},
   "outputs": [],
   "source": [
    "# config['bot']['long']['n_positions'] = 3\n",
    "# config['bot']['long']['filter_rolling_window'] = 10\n",
    "# config['bot']['long']['filter_relative_volume_clip_pct'] = 0.5\n",
    "# config['bot']['short']['n_positions'] = 0.0\n",
    "# config['backtest']['btc_collateral_cap'] = 0.7\n",
    "#config['bot']['long']['close_trailing_threshold_pct'] = 0.004\n",
    "#config['bot']['long']['close_trailing_retracement_pct'] = 0.004"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ef8d0f05-37ab-4a56-ab17-07a3cd0d70b7",
   "metadata": {},
   "outputs": [],
   "source": [
    "fills, equities, analysis = execute_backtest(payload, config)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "87e20b2a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Keep every other coin to probe subset performance\n",
    "coin_indices = list(range(0, payload.bundle.coins_len(), 2))\n",
    "subset_payload = subset_backtest_payload(payload, coin_indices=coin_indices)\n",
    "subset_fills, subset_equities, subset_analysis = execute_backtest(subset_payload, config)\n",
    "subset_analysis[\"adg_btc\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2402fe86",
   "metadata": {},
   "source": [
    "The helper above makes it easy to carve out ad-hoc slices of the HLCV cube. You can also target specific tickers explicitly:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fa51a693-0299-4c2c-8395-e458584af115",
   "metadata": {},
   "outputs": [],
   "source": [
    "fdf, analysis_py, bal_eq = process_forager_fills(\n",
    "    subset_fills,\n",
    "    config[\"backtest\"][\"coins\"][exchange],\n",
    "    subset_payload.bundle.hlcvs,\n",
    "    subset_equities,\n",
    ")\n",
    "fdf.loc[:,'we'] = fdf.psize * fdf.pprice / fdf.usd_total_balance\n",
    "delta = fdf.we - fdf.we.groupby(fdf[\"coin\"]).shift(fill_value=0.0)\n",
    "fdf.loc[:,\"twe\"] = delta.cumsum()\n",
    "for k in analysis_py:\n",
    "    if k not in subset_analysis:\n",
    "        subset_analysis[k] = analysis_py[k]\n",
    "display_keys = {\n",
    "    'adg_btc',\n",
    "    'adg_usd',\n",
    "    'drawdown_worst_btc',\n",
    "    'drawdown_worst_usd',\n",
    "    'gain_btc',\n",
    "    'gain_usd',\n",
    "    \"peak_recovery_hours_pnl\",\n",
    "    \"peak_recovery_hours_equity_usd\",\n",
    "    \"peak_recovery_hours_equity_btc\",\n",
    "    \"sharpe_ratio_btc\",\n",
    "    \"sharpe_ratio_usd\",\n",
    "    \"position_held_hours_max\",\n",
    "    \"position_unchanged_hours_max\",\n",
    "}\n",
    "pprint.pprint({k: v for k, v in subset_analysis.items() if k in display_keys})\n",
    "_ = create_forager_balance_figures(bal_eq, fast=True, stride=10, logy=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "84c2ad73",
   "metadata": {},
   "outputs": [],
   "source": [
    "focus_payload = subset_backtest_payload(\n",
    "    payload,\n",
    "    coin_symbols=[\"BTC/USDT:USDT\", \"ETH/USDT:USDT\", \"XRP/USDT:USDT\", \"ADA/USDT:USDT\", \"SOL/USDT:USDT\"],\n",
    ")\n",
    "focus_fills, focus_equities, focus_analysis = execute_backtest(focus_payload, config)\n",
    "focus_analysis[\"adg_btc\"]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8ef1706a-8687-4c88-97e7-4d73b5146ca3",
   "metadata": {},
   "outputs": [],
   "source": [
    "fdf, analysis_py, bal_eq = process_forager_fills(\n",
    "    focus_fills,\n",
    "    config[\"backtest\"][\"coins\"][exchange],\n",
    "    focus_payload.bundle.hlcvs,\n",
    "    focus_equities,\n",
    ")\n",
    "fdf.loc[:,'we'] = fdf.psize * fdf.pprice / fdf.usd_total_balance\n",
    "delta = fdf.we - fdf.we.groupby(fdf[\"coin\"]).shift(fill_value=0.0)\n",
    "fdf.loc[:,\"twe\"] = delta.cumsum()\n",
    "for k in analysis_py:\n",
    "    if k not in focus_analysis:\n",
    "        focus_analysis[k] = analysis_py[k]\n",
    "display_keys = {\n",
    "    'adg_btc',\n",
    "    'adg_usd',\n",
    "    'drawdown_worst_btc',\n",
    "    'drawdown_worst_usd',\n",
    "    'gain_btc',\n",
    "    'gain_usd',\n",
    "    \"peak_recovery_hours_pnl\",\n",
    "    \"peak_recovery_hours_equity_usd\",\n",
    "    \"peak_recovery_hours_equity_btc\",\n",
    "    \"sharpe_ratio_btc\",\n",
    "    \"sharpe_ratio_usd\",\n",
    "    \"position_held_hours_max\",\n",
    "    \"position_unchanged_hours_max\",\n",
    "}\n",
    "pprint.pprint({k: v for k, v in focus_analysis.items() if k in display_keys})\n",
    "_ = create_forager_balance_figures(bal_eq, fast=True, stride=10, logy=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6ff5b75b-7e2b-4a60-9672-e16cc54219a0",
   "metadata": {},
   "outputs": [],
   "source": [
    "payload.bundle.hlcvs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b0a86c90-e834-4016-abfa-8158e2d227cb",
   "metadata": {},
   "outputs": [],
   "source": [
    "fdf, analysis_py, bal_eq = process_forager_fills(\n",
    "    fills,\n",
    "    config[\"backtest\"][\"coins\"][exchange],\n",
    "    hlcvs,\n",
    "    equities,\n",
    ")\n",
    "fdf.loc[:,'we'] = fdf.psize * fdf.pprice / fdf.usd_total_balance\n",
    "delta = fdf.we - fdf.we.groupby(fdf[\"coin\"]).shift(fill_value=0.0)\n",
    "fdf.loc[:,\"twe\"] = delta.cumsum()\n",
    "for k in analysis_py:\n",
    "    if k not in analysis:\n",
    "        analysis[k] = analysis_py[k]\n",
    "display_keys = {\n",
    "    'adg_btc',\n",
    "    'adg_usd',\n",
    "    'drawdown_worst_btc',\n",
    "    'drawdown_worst_usd',\n",
    "    'gain_btc',\n",
    "    'gain_usd',\n",
    "    \"peak_recovery_hours_pnl\",\n",
    "    \"peak_recovery_hours_equity_usd\",\n",
    "    \"peak_recovery_hours_equity_btc\",\n",
    "    \"sharpe_ratio_btc\",\n",
    "    \"sharpe_ratio_usd\",\n",
    "    \"position_held_hours_max\",\n",
    "    \"position_unchanged_hours_max\",\n",
    "}\n",
    "pprint.pprint({k: v for k, v in analysis.items() if k in display_keys})\n",
    "_ = create_forager_balance_figures(bal_eq, fast=True, stride=10, logy=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cc95c575-e29f-4cc7-aef3-b8c7c49766ee",
   "metadata": {},
   "outputs": [],
   "source": [
    "fdf.twe.plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d670be01-eafd-446c-a2ed-946f4dd8d6aa",
   "metadata": {},
   "outputs": [],
   "source": [
    "opened_pos = defaultdict(float)\n",
    "durations = []\n",
    "for row in fdf.itertuples():\n",
    "    if row.coin in opened_pos:\n",
    "        if row.psize == 0.0:\n",
    "            durations.append((row.coin, row.timestamp.timestamp() - opened_pos[row.coin]))\n",
    "            del opened_pos[row.coin]\n",
    "    else:\n",
    "        if row.psize != 0.0:\n",
    "            opened_pos[row.coin] = row.timestamp.timestamp()\n",
    "max_durations = defaultdict(float)\n",
    "for x in durations:\n",
    "    max_durations[x[0]] = max(x[1], max_durations[x[0]])\n",
    "sorted(max_durations.items(), key=lambda x: x[1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7aa52402-c254-4ffa-8393-cef7f9428b1c",
   "metadata": {},
   "outputs": [],
   "source": [
    "coins_sorted_by_volume = fdf.groupby(\"coin\").fee_paid.sum().sort_values().index.to_list()\n",
    "coin = coins_sorted_by_volume[0]\n",
    "coin = 'APT'\n",
    "print(coin)\n",
    "cdf, fdfc = plot_fills_long(hlcvs, fdf, coins, coin, start_pct=0.0, end_pct=1.0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9d7e6fac-a633-4365-a419-24a4b32533ce",
   "metadata": {},
   "outputs": [],
   "source": [
    "json.dumps(config['live']['approved_coins']['long'][1::2])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7db62407-a7f4-4776-bc32-2c67b73680db",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "93a8c7de-b1a3-4cda-a4ed-4f1a43bf5578",
   "metadata": {},
   "outputs": [],
   "source": [
    "# performers worst to best\n",
    "for x in fdf.groupby(\"coin\").pnl.sum().sort_values().to_dict().items():\n",
    "    print(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8aca63bb-6ce0-4f38-80aa-f1c26bbfbfd6",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python (venv)",
   "language": "python",
   "name": "venv"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
